from decimal import Decimal

from django.db import connection, transaction

from app.decorator import jwt_auth
from app.model.cs_carts import CsCarts
from app.my_exception import MyException
from app.service.goods import query_goods_in_ids
from app.service import app_order, order, cart as csCart, payment, shop, member
from libs import payment_lib
from libs.common import success, error, get_ts
from libs.form import RawJsonForm
from libs.time_helper import ts_to_time_str


@jwt_auth
def create_order(request, auth):
    form = RawJsonForm(request.body)
    bill_num = form.get('bill_num')
    pay_code = form.get('pay_code', default=None)
    post_member_id = form.get_int('member_id')
    shop_id = auth.get('shop_id')
    cashier_id = auth.get('cashier_id')
    resp_data = {
        'total_price': 0,
        'order_sn': ''
    }
    pay_typ_dic = order.PaymentMethodDict
    pay_methods = form.get_list('pay_methods')
    pay_method_len = len(pay_methods)
    if pay_method_len == 0:
        return error('请至少选择一种付款方式')
    pay_result = {}
    pay_total = Decimal('0')
    selected_pay_method_dic = {}
    try:
        pay_method_arr = []
        pay_methods = order.sort_pay_methods(pay_methods)
        for pay_data in pay_methods:
            val_typ = type(pay_data)
            if val_typ is not dict:
                return error('存在无效的付款方式')
            pay_typ = int(pay_data.get('typ'))
            if pay_typ not in pay_typ_dic:
                return error('存在无效的付款方式')
            pay_method_arr.append(pay_typ_dic.get(pay_typ))
            pay_amount = Decimal(str(pay_data.get('amount'))).quantize(Decimal('0.00'))
            if pay_amount < 0:
                return error('付款总额不能小于0')
            pay_total += pay_amount
            selected_pay_method_dic[pay_typ] = {'pay_amount': pay_amount}
        pay_method = '+'.join(pay_method_arr)
    except Exception as e:
        err = '存在无效的付款方式'
        print(err, e)
        return error(err)

    filters = {'shop_id': shop_id, 'bill_num': bill_num, 'status__lte': 0, 'deleted': 0}
    total = CsCarts.objects.filter(**filters).count()
    third_party_pay_flag = 0  # 支付类型ID
    third_party_pay_data = {}  # 第三方支付
    order_status = 1  # 默认订单状态为已结，如有第三方支付，状态为未结
    if total > 0:
        # 现金、微信/支付宝、线下扫码
        # 微信/支付宝：用户扫商铺
        # 线下扫码：商铺扫用户
        # selected_pay_method_num = len(selected_pay_method_dic.keys())
        # 如果使用了仓储卡结算，并且结算金额>0，但没有提供会员id，提示错误
        if 2 in selected_pay_method_dic and selected_pay_method_dic[2].get('pay_amount') > 0:
            if post_member_id <= 0:
                return error('支付方式中包含“仓储卡”，需要提供会员信息')
            # if selected_pay_method_num > 1:
            #     return error('仓储卡不支持组合支付')

        if 3 in selected_pay_method_dic and selected_pay_method_dic[3].get('pay_amount') > 0 and post_member_id <= 0:
            return error('支付方式中包含“余额”，需要提供会员信息')

        if 100 in selected_pay_method_dic:
            order_status = 0
            if pay_code is None:
                if selected_pay_method_dic[100].get('pay_amount') > 0:
                    return error('支付方式中包含“微信/支付宝”，需要提供付款码')
            else:
                pay_code_info = payment_lib.get_pay_code_type(pay_code)
                if pay_code_info is None:
                    return error('无效的第三方支付方式')
                elif type(pay_code_info) is dict:
                    third_party_pay_flag = pay_code_info.get('code')
            selected_pay_method_dic[100].update({'third_party_pay_flag': third_party_pay_flag})

        carts = CsCarts.objects.filter(**filters).all().values()
        goods_num = 0
        cart_total_price = Decimal(0)
        now = get_ts()
        goods_id_arr = []
        for cart in carts:
            goods_num += cart.get('amount')
            cart_subtotal = cart.get('subtotal')
            cart.update({'subtotal_raw': cart_subtotal})
            cart_total_price += cart_subtotal
            cart_goods_id = cart.get('goods_id')
            if cart_goods_id > 0:
                goods_id_arr.append(str(cart_goods_id))

        cart_total_price_dec = cart_total_price.quantize(Decimal('0.00'))  # 购物车总金额
        pay_total_dec = pay_total.quantize(Decimal('0.00'))  # 用户实际支付金额
        cart_total_price_str = str(cart_total_price_dec)
        # if total_price_dec > pay_total_dec:
        #     return error('支付金额(￥{})小于订单金额(￥{})'.format(total_price_str, pay_total))

        post_total_amount = form.get('total_amount', default=None)  # 用户提交的支付金额
        # 未提交商品总额，按照购物车计算出来的金额结算
        if post_total_amount is None:
            if cart_total_price_dec != pay_total_dec:
                return error('订单金额(￥{})与支付金额(￥{})不符'.format(cart_total_price_str, pay_total))
            total_amount_dec = pay_total_dec
        else:
            try:
                post_total_amount_dec = Decimal(post_total_amount).quantize(Decimal('0.00'))
                if post_total_amount_dec < 0:
                    return error('支付金额不可小于0')
                # 指定订单金额为0
                if post_total_amount_dec == 0 or cart_total_price_dec == 0:
                    for cart in carts:
                        cart.update({'subtotal': 0})
                else:
                    remaining_dec = post_total_amount_dec - cart_total_price_dec  # 剩余，可为负数
                    if remaining_dec > 0 or remaining_dec < 0:
                        remaining_ratio_dec = (remaining_dec / cart_total_price_dec) + Decimal('1')
                        # 会出现除不尽
                        for cart in carts:
                            updated_subtotal = (cart.get('subtotal') * remaining_ratio_dec).quantize(Decimal('0.00'))
                            cart.update({'subtotal': updated_subtotal})
                total_amount_dec = post_total_amount_dec
            except Exception:
                return error('无效的支付金额')

        if pay_total_dec < total_amount_dec:
            return error('支付金额小于商品总额')

        total_amount_str = str(total_amount_dec)  # 收银输入的总金额，默认等于支付总金额

        # 获取购物车内的商品字典
        goods_dict = query_goods_in_ids(goods_id_arr)
        cursor = connection.cursor()
        try:
            with transaction.atomic():
                # 提供会员ID
                # 扣减余额、写入流水(仅仓储卡)
                # 获得积分
                # 如果包含仓储卡并且提供会员ID
                valid_mem_id = 0
                db_mem = {}
                mem_username = ''
                if post_member_id > 0:
                    db_mem = member.get_member_with_cursor(cursor, post_member_id)
                    db_mem_id = db_mem.get('id', 0)
                    mem_username = db_mem.get('username', '')
                    if db_mem_id == 0:
                        raise MyException('会员信息不存在')
                    valid_mem_id = db_mem_id

                # 组合支付
                order_sn = order.gen_order_sn()
                pay_method_counter = 0
                is_mixed_pay = 0
                storage_pay_amount_dec = Decimal('0')  # 仓储卡支付金额
                member_fund_update_counter = 0
                app_pay_mode = 0
                app_pay_mode_map = app_order.APP_PAY_MODE_MAP
                payAmountAllocFlag = False  # 支付金额分配完成标记
                total_amount_counter_dec = Decimal('0')
                for pay_method_typ in selected_pay_method_dic:
                    pay_method_counter += 1
                    payAmountDec = selected_pay_method_dic[pay_method_typ].get('pay_amount')
                    if payAmountDec > 0:
                        nextCounterDec = total_amount_counter_dec + payAmountDec

                        if payAmountAllocFlag:
                            del (selected_pay_method_dic[pay_method_typ])
                            break

                        # 如果提交的支付总额超过商品总额，标记支付分配完成
                        if nextCounterDec >= total_amount_dec:
                            payAmountAllocFlag = True
                            payAmountDec = total_amount_dec - total_amount_counter_dec
                            selected_pay_method_dic[pay_method_typ].update({'pay_amount': payAmountDec})

                        if pay_method_typ == 1:  # 现金
                            app_pay_mode = 4
                            selected_pay_method_dic[pay_method_typ].update({'confirmed': now})
                        elif pay_method_typ == 2:  # 仓储卡
                            app_pay_mode = 5
                            selected_pay_method_dic[pay_method_typ].update({'confirmed': now})
                            if valid_mem_id == 0:
                                raise MyException('使用仓储卡支付，请提供有效会员信息')
                            if member_fund_update_counter > 0:
                                db_mem.update({'ver': db_mem.get('ver') + member_fund_update_counter})
                            # 处理仓储卡支付
                            proc_err = order.process_member_storage_fund(cursor, db_mem, payAmountDec, 'reduce')
                            if proc_err is not None:
                                raise MyException(proc_err)
                            storage_pay_amount_dec = payAmountDec
                            member_fund_update_counter += 1
                        elif pay_method_typ == 3:  # 余额
                            app_pay_mode = 1
                            selected_pay_method_dic[pay_method_typ].update({'confirmed': now})
                            if valid_mem_id == 0:
                                raise MyException('使用会员余额支付，请提供有效会员信息')
                            if member_fund_update_counter > 0:
                                db_mem.update({'ver': db_mem.get('ver') + member_fund_update_counter})
                            # 处理余额支付
                            proc_err = order.process_member_balance_fund(cursor, db_mem, payAmountDec, 'reduce')
                            if proc_err is not None:
                                raise MyException(proc_err)
                            member_fund_update_counter += 1
                        elif pay_method_typ == 10:  # 线下扫码
                            app_pay_mode = 7
                            selected_pay_method_dic[pay_method_typ].update({'confirmed': now})
                            # 处理线下扫码支付
                            proc_err = order.process_scan_payment()
                            if proc_err is not None:
                                raise MyException(proc_err)
                        elif pay_method_typ == 100:  # 微信/支付宝
                            if third_party_pay_flag == 1:
                                app_pay_mode = 2
                            elif third_party_pay_flag == 2:
                                app_pay_mode = 3

                            pay_fen = int(payAmountDec * Decimal('100'))
                            third_party_pay_data.update({'pay_fen': pay_fen, 'order_sn': order_sn})
                            selected_pay_method_dic[pay_method_typ].update({'confirmed': 0})
                            # 处理微信/支付宝支付
                            proc_err = order.process_wx_or_ali_payment()
                            if proc_err is not None:
                                raise MyException(proc_err)
                        else:
                            raise MyException('无效的支付方式')
                if pay_method_counter > 1:
                    is_mixed_pay = 1
                    app_pay_mode = 99

                # 支付方式包括仓储卡
                stats_total_price_dec = Decimal(0)  # 总金额扣除仓储卡支付的金额 app 订单统计用
                if storage_pay_amount_dec > 0:
                    stats_total_price_dec = pay_total_dec - storage_pay_amount_dec

                # 添加记录到APP订单
                db_shop = shop.get_shop(shop_id, ['shop_name', 'shop_phone', 'shop_address'])
                shop_name = db_shop.get('shop_name', '')
                datetime_str = ts_to_time_str(time_format="%Y-%m-%d %H:%M:%S")
                insert_order_bindings = [shop_id, cashier_id, order_sn,
                                         bill_num, pay_method, post_member_id,
                                         mem_username, goods_num, total_amount_str,
                                         cart_total_price_str, order_status, now, now]
                cs_order_id = order.insert_order(cursor, insert_order_bindings)
                only_pay_no = app_pay_mode_map.get(app_pay_mode, '')

                pay_time = ''
                if third_party_pay_flag > 0:
                    pay_time = datetime_str
                app_order_data = {
                    'order_id': order_sn,
                    'member_id': mem_username,
                    'shoper_name': shop_name,
                    'order_time': datetime_str,
                    'total': total_amount_str,
                    'pay_usdt': total_amount_str,
                    'offline_order_id': order_sn,
                    'cs_order_id': cs_order_id,
                    'is_mixed_pay': is_mixed_pay,
                    'stats_total_price': str(stats_total_price_dec),
                    'pay_mode': app_pay_mode,
                    'only_pay_no': only_pay_no,
                    'send_shop_id': shop_id
                }
                if pay_time != '':
                    app_order_data.update({'pay_time': pay_time})
                if pay_method_typ == 100:
                    app_order_data.update({'state': 0})
                # 保存订单
                app_order_id, err = app_order.save_app_order(cursor, app_order_data)
                if err is not None:
                    raise MyException(err)
                # 批量添加订单详情
                insert_order_detail_sql = 'insert into cs_order_details' \
                                          ' (order_id,goods_id,goods_name,barcode,' \
                                          'amount,selling_price,spec_typ,is_input_price,' \
                                          'is_discount,subtotal_raw,subtotal,stock_prev,' \
                                          'stock_next,created,updated) values '
                # 处理库存，扣减库存
                values_arr, action_code, err = csCart.settlement(cursor, carts, goods_dict, app_order_data, stats_total_price_dec)
                if err is not None:
                    raise MyException(err)
                if len(values_arr) > 0:
                    values = ','.join(values_arr)
                    insert_order_detail_sql += values
                    cursor.execute(insert_order_detail_sql)
                else:
                    raise MyException('订单创建异常[110101]')

                # 记录订单支付日志
                pay_log_dict, err = order.save_order_pay_logs(cashier_id, cs_order_id, selected_pay_method_dic, cursor)
                if err is not None:
                    raise MyException(err)

                update_cart_sql = 'update cs_carts set `status`=1 where bill_num=%s'
                cursor.execute(update_cart_sql, bill_num)
                resp_data.update({'total_price': post_total_amount})
                resp_data.update({'cart_total_price': cart_total_price_str})
                resp_data.update({'order_sn': order_sn})
                order_result = order.get_order_detail(cursor, cs_order_id)
        except MyException as e:
            return error(str(e))
        except Exception as e:
            print(e)
            return error('订单创建异常[110102]')
        finally:
            cursor.close()

        if pay_code is not None:
            # 处理第三方支付，第三方支付库存已扣，如确认支付失败，需要恢复库存
            if 100 in pay_log_dict:
                pay_log_id = pay_log_dict[100]
                pay_result, err = payment.process_cart_payment(cashier_id,
                                                               cs_order_id,
                                                               order_sn,
                                                               pay_log_id,
                                                               third_party_pay_flag,
                                                               pay_code,
                                                               third_party_pay_data)
                if err is not None:
                    return error(err)
    else:
        return error('没有需要结算的商品')
    return success(resp_data, detail=order_result, shop=db_shop, order_sn=order_sn, **pay_result)
